home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkFont.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-15  |  21.7 KB  |  774 lines

  1. /* 
  2.  * tkFont.c --
  3.  *
  4.  *    This file maintains a database of looked-up fonts for the Tk
  5.  *    toolkit, in order to avoid round-trips to the server to map
  6.  *    font names to XFontStructs.
  7.  *
  8.  * Copyright 1990 Regents of the University of California
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkFont.c,v 1.21 92/06/15 14:00:19 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include "tkConfig.h"
  23. #include "tkInt.h"
  24.  
  25. /*
  26.  * This module caches extra information about fonts in addition to
  27.  * what X already provides.  The extra information is used by the
  28.  * TkMeasureChars procedure, and consists of two parts:  a type and
  29.  * a width.  The type is one of the following:
  30.  *
  31.  * NORMAL:        Standard character.
  32.  * TAB:            Tab character:  output enough space to
  33.  *            get to next tab stop.
  34.  * NEWLINE:        Newline character:  don't output anything more
  35.  *            on this line (character has infinite width).
  36.  * REPLACE:        This character doesn't print:  instead of
  37.  *            displaying character, display a replacement
  38.  *            sequence of the form "\xdd" where dd is the
  39.  *            hex equivalent of the character.
  40.  * SKIP:        Don't display anything for this character.  This
  41.  *            is only used where the font doesn't contain
  42.  *            all the characters needed to generate
  43.  *            replacement sequences.
  44.  * The width gives the total width of the displayed character or
  45.  * sequence:  for replacement sequences, it gives the width of the
  46.  * sequence.
  47.  */
  48.  
  49. #define NORMAL        1
  50. #define TAB        2
  51. #define NEWLINE        3
  52. #define REPLACE        4
  53. #define SKIP        5
  54.  
  55. /*
  56.  * One of the following data structures exists for each font that is
  57.  * currently active.  The structure is indexed with two hash tables,
  58.  * one based on font name and one based on XFontStruct address.
  59.  */
  60.  
  61. typedef struct {
  62.     XFontStruct *fontStructPtr;    /* X information about font. */
  63.     Display *display;        /* Display to which font belongs. */
  64.     int refCount;        /* Number of active uses of this font. */
  65.     char *types;        /* Malloc'ed array giving types of all
  66.                  * chars in the font (may be NULL). */
  67.     unsigned char *widths;    /* Malloc'ed array giving widths of all
  68.                  * chars in the font (may be NULL). */
  69.     int tabWidth;        /* Width of tabs in this font. */
  70.     Tcl_HashEntry *nameHashPtr;    /* Entry in name-based hash table (needed
  71.                  * when deleting this structure). */
  72. } TkFont;
  73.  
  74. /*
  75.  * Hash table for name -> TkFont mapping, and key structure used to
  76.  * index into that table:
  77.  */
  78.  
  79. static Tcl_HashTable nameTable;
  80. typedef struct {
  81.     Tk_Uid name;        /* Name of font. */
  82.     Display *display;        /* Display for which font is valid. */
  83. } NameKey;
  84.  
  85. /*
  86.  * Hash table for font struct -> TkFont mapping. This table is
  87.  * indexed by the XFontStruct address.
  88.  */
  89.  
  90. static Tcl_HashTable fontTable;
  91.  
  92. static int initialized = 0;    /* 0 means static structures haven't been
  93.                  * initialized yet. */
  94.  
  95. /*
  96.  * To speed up TkMeasureChars, the variables below keep the last
  97.  * mapping from (XFontStruct *) to (TkFont *).
  98.  */
  99.  
  100. static TkFont *lastFontPtr = NULL;
  101. static XFontStruct *lastFontStructPtr = NULL;
  102.  
  103. /*
  104.  * Characters used when displaying control sequences as their
  105.  * hex equivalents.
  106.  */
  107.  
  108. static char hexChars[] = "0123456789abcdefx\\";
  109.  
  110. /*
  111.  * Forward declarations for procedures defined in this file:
  112.  */
  113.  
  114. static void        FontInit _ANSI_ARGS_((void));
  115. static void        SetFontMetrics _ANSI_ARGS_((TkFont *fontPtr));
  116.  
  117. /*
  118.  *----------------------------------------------------------------------
  119.  *
  120.  * Tk_GetFontStruct --
  121.  *
  122.  *    Given a string name for a font, map the name to an XFontStruct
  123.  *    describing the font.
  124.  *
  125.  * Results:
  126.  *    The return value is normally a pointer to the font description
  127.  *    for the desired font.  If an error occurs in mapping the string
  128.  *    to a font, then an error message will be left in interp->result
  129.  *    and NULL will be returned.
  130.  *
  131.  * Side effects:
  132.  *    The font is added to an internal database with a reference count.
  133.  *    For each call to this procedure, there should eventually be a call
  134.  *    to Tk_FreeFontStruct, so that the database is cleaned up when fonts
  135.  *    aren't in use anymore.
  136.  *
  137.  *----------------------------------------------------------------------
  138.  */
  139.  
  140. XFontStruct *
  141. Tk_GetFontStruct(interp, tkwin, name)
  142.     Tcl_Interp *interp;        /* Place to leave error message if
  143.                  * font can't be found. */
  144.     Tk_Window tkwin;        /* Window in which font will be used. */
  145.     Tk_Uid name;        /* Name of font (in form suitable for
  146.                  * passing to XLoadQueryFont). */
  147. {
  148.     NameKey nameKey;
  149.     Tcl_HashEntry *nameHashPtr, *fontHashPtr;
  150.     int new;
  151.     register TkFont *fontPtr;
  152.     XFontStruct *fontStructPtr;
  153.  
  154.     if (!initialized) {
  155.     FontInit();
  156.     }
  157.  
  158.     /*
  159.      * First, check to see if there's already a mapping for this font
  160.      * name.
  161.      */
  162.  
  163.     nameKey.name = name;
  164.     nameKey.display = Tk_Display(tkwin);
  165.     nameHashPtr = Tcl_CreateHashEntry(&nameTable, (char *) &nameKey, &new);
  166.     if (!new) {
  167.     fontPtr = (TkFont *) Tcl_GetHashValue(nameHashPtr);
  168.     fontPtr->refCount++;
  169.     return fontPtr->fontStructPtr;
  170.     }
  171.  
  172.     /*
  173.      * The name isn't currently known.  Map from the name to a font, and
  174.      * add a new structure to the database.
  175.      */
  176.  
  177.     fontStructPtr = XLoadQueryFont(nameKey.display, name);
  178.     if (fontStructPtr == NULL) {
  179.     Tcl_DeleteHashEntry(nameHashPtr);
  180.     Tcl_AppendResult(interp, "font \"", name, "\" doesn't exist",
  181.         (char *) NULL);
  182.     return NULL;
  183.     }
  184.     fontPtr = (TkFont *) ckalloc(sizeof(TkFont));
  185.     fontPtr->display = nameKey.display;
  186.     fontPtr->fontStructPtr = fontStructPtr;
  187.     fontPtr->refCount = 1;
  188.     fontPtr->types = NULL;
  189.     fontPtr->widths = NULL;
  190.     fontPtr->nameHashPtr = nameHashPtr;
  191.     fontHashPtr = Tcl_CreateHashEntry(&fontTable, (char *) fontStructPtr, &new);
  192.     if (!new) {
  193.     panic("XFontStruct already registered in Tk_GetFontStruct");
  194.     }
  195.     Tcl_SetHashValue(nameHashPtr, fontPtr);
  196.     Tcl_SetHashValue(fontHashPtr, fontPtr);
  197.     return fontPtr->fontStructPtr;
  198. }
  199.  
  200. /*
  201.  *--------------------------------------------------------------
  202.  *
  203.  * Tk_NameOfFontStruct --
  204.  *
  205.  *    Given a font, return a textual string identifying it.
  206.  *
  207.  * Results:
  208.  *    If font was created by Tk_GetFontStruct, then the return
  209.  *    value is the "string" that was used to create it.
  210.  *    Otherwise the return value is a string giving the X
  211.  *    identifier for the font.  The storage for the returned
  212.  *    string is only guaranteed to persist up until the next
  213.  *    call to this procedure.
  214.  *
  215.  * Side effects:
  216.  *    None.
  217.  *
  218.  *--------------------------------------------------------------
  219.  */
  220.  
  221. char *
  222. Tk_NameOfFontStruct(fontStructPtr)
  223.     XFontStruct *fontStructPtr;        /* Font whose name is desired. */
  224. {
  225.     Tcl_HashEntry *fontHashPtr;
  226.     TkFont *fontPtr;
  227.     static char string[20];
  228.  
  229.     if (!initialized) {
  230.     printid:
  231.     sprintf(string, "font id 0x%x", fontStructPtr->fid);
  232.     return string;
  233.     }
  234.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  235.     if (fontHashPtr == NULL) {
  236.     goto printid;
  237.     }
  238.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  239.     return ((NameKey *) fontPtr->nameHashPtr->key.words)->name;
  240. }
  241.  
  242. /*
  243.  *----------------------------------------------------------------------
  244.  *
  245.  * Tk_FreeFontStruct --
  246.  *
  247.  *    This procedure is called to release a font allocated by
  248.  *    Tk_GetFontStruct.
  249.  *
  250.  * Results:
  251.  *    None.
  252.  *
  253.  * Side effects:
  254.  *    The reference count associated with font is decremented, and
  255.  *    the font is officially deallocated if no-one is using it
  256.  *    anymore.
  257.  *
  258.  *----------------------------------------------------------------------
  259.  */
  260.  
  261. void
  262. Tk_FreeFontStruct(fontStructPtr)
  263.     XFontStruct *fontStructPtr;    /* Font to be released. */
  264. {
  265.     Tcl_HashEntry *fontHashPtr;
  266.     register TkFont *fontPtr;
  267.  
  268.     if (!initialized) {
  269.     panic("Tk_FreeFontStruct called before Tk_GetFontStruct");
  270.     }
  271.  
  272.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  273.     if (fontHashPtr == NULL) {
  274.     panic("Tk_FreeFontStruct received unknown font argument");
  275.     }
  276.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  277.     fontPtr->refCount--;
  278.     if (fontPtr->refCount == 0) {
  279.     XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
  280.     Tcl_DeleteHashEntry(fontPtr->nameHashPtr);
  281.     Tcl_DeleteHashEntry(fontHashPtr);
  282.     if (fontPtr->types != NULL) {
  283.         ckfree(fontPtr->types);
  284.     }
  285.     if (fontPtr->widths != NULL) {
  286.         ckfree((char *) fontPtr->widths);
  287.     }
  288.     ckfree((char *) fontPtr);
  289.     lastFontStructPtr = NULL;
  290.     }
  291. }
  292.  
  293. /*
  294.  *----------------------------------------------------------------------
  295.  *
  296.  * FontInit --
  297.  *
  298.  *    Initialize the structure used for font management.
  299.  *
  300.  * Results:
  301.  *    None.
  302.  *
  303.  * Side effects:
  304.  *    Read the code.
  305.  *
  306.  *----------------------------------------------------------------------
  307.  */
  308.  
  309. static void
  310. FontInit()
  311. {
  312.     initialized = 1;
  313.     Tcl_InitHashTable(&nameTable, sizeof(NameKey)/sizeof(int));
  314.     Tcl_InitHashTable(&fontTable, TCL_ONE_WORD_KEYS);
  315. }
  316.  
  317. /*
  318.  *--------------------------------------------------------------
  319.  *
  320.  * SetFontMetrics --
  321.  *
  322.  *    This procedure is called to fill in the "widths" and "types"
  323.  *    arrays for a font.
  324.  *
  325.  * Results:
  326.  *    None.
  327.  *
  328.  * Side effects:
  329.  *    FontPtr gets modified to hold font metric information.
  330.  *
  331.  *--------------------------------------------------------------
  332.  */
  333.  
  334. static void
  335. SetFontMetrics(fontPtr)
  336.     register TkFont *fontPtr;        /* Font structure in which to
  337.                      * set metrics. */
  338. {
  339.     int i, replaceOK, baseWidth;
  340.     register XFontStruct *fontStructPtr = fontPtr->fontStructPtr;
  341.     char *p;
  342.  
  343.     /*
  344.      * Pass 1: initialize the arrays.
  345.      */
  346.  
  347.     fontPtr->types = (char *) ckalloc(256);
  348.     fontPtr->widths = (unsigned char *) ckalloc(256);
  349.     for (i = 0; i < 256; i++) {
  350.     fontPtr->types[i] = REPLACE;
  351.     }
  352.  
  353.     /*
  354.      * Pass 2:  for all characters that exist in the font and are
  355.      * not control characters, fill in the type and width
  356.      * information.
  357.      */
  358.  
  359.     for (i = ' '; i < 256;  i++) {
  360.     if ((i == 0177) || (i < fontStructPtr->min_char_or_byte2)
  361.         || (i > fontStructPtr->max_char_or_byte2)) {
  362.         continue;
  363.     }
  364.     fontPtr->types[i] = NORMAL;
  365.     if (fontStructPtr->per_char == NULL) {
  366.         fontPtr->widths[i] = fontStructPtr->min_bounds.width;
  367.     } else {
  368.         fontPtr->widths[i] = fontStructPtr->per_char[i
  369.             - fontStructPtr->min_char_or_byte2].width;
  370.     }
  371.     }
  372.  
  373.     /*
  374.      * Pass 3: fill in information for characters that have to
  375.      * be replaced with  "\xhh" strings.  If the font doesn't
  376.      * have the characters needed for this, then just use the
  377.      * font's default character.
  378.      */
  379.  
  380.     replaceOK = 1;
  381.     baseWidth = fontPtr->widths['\\'] + fontPtr->widths['x'];
  382.     for (p = hexChars; *p != 0; p++) {
  383.     if (fontPtr->types[*p] != NORMAL) {
  384.         replaceOK = 0;
  385.         break;
  386.     }
  387.     }
  388.     for (i = 0; i < 256; i++) {
  389.     if (fontPtr->types[i] != REPLACE) {
  390.         continue;
  391.     }
  392.     if (replaceOK) {
  393.         fontPtr->widths[i] = baseWidth
  394.             + fontPtr->widths[hexChars[i & 0xf]]
  395.             + fontPtr->widths[hexChars[(i>>4) & 0xf]];
  396.     } else {
  397.         fontPtr->types[i] = SKIP;
  398.         fontPtr->widths[i] = 0;
  399.     }
  400.     }
  401.  
  402.     /*
  403.      * Lastly, fill in special information for newline and tab.
  404.      */
  405.  
  406.     fontPtr->types['\n'] = NEWLINE;
  407.     fontPtr->widths['\n'] = 0;
  408.     fontPtr->types['\t'] = TAB;
  409.     fontPtr->widths['\t'] = 0;
  410.     if (fontPtr->types['0'] == NORMAL) {
  411.     fontPtr->tabWidth = 8*fontPtr->widths['0'];
  412.     } else {
  413.     fontPtr->tabWidth = 8*fontStructPtr->max_bounds.width;
  414.     }
  415.  
  416.     /*
  417.      * Make sure the tab width isn't zero (some fonts may not have enough
  418.      * information to set a reasonable tab width).
  419.      */
  420.  
  421.     if (fontPtr->tabWidth == 0) {
  422.     fontPtr->tabWidth = 1;
  423.     }
  424. }
  425.  
  426. /*
  427.  *--------------------------------------------------------------
  428.  *
  429.  * TkMeasureChars --
  430.  *
  431.  *    Measure the number of characters from a string that
  432.  *    will fit in a given horizontal span.  The measurement
  433.  *    is done under the assumption that TkDisplayChars will
  434.  *    be used to actually display the characters.
  435.  *
  436.  * Results:
  437.  *    The return value is the number of characters from source
  438.  *    that fit in the span given by startX and maxX.  *nextXPtr
  439.  *    is filled in with the x-coordinate at which the first
  440.  *    character that didn't fit would be drawn, if it were to
  441.  *    be drawn.
  442.  *
  443.  * Side effects:
  444.  *    None.
  445.  *
  446.  *--------------------------------------------------------------
  447.  */
  448.  
  449. int
  450. TkMeasureChars(fontStructPtr, source, maxChars, startX, maxX, flags, nextXPtr)
  451.     XFontStruct *fontStructPtr;    /* Font in which to draw characters. */
  452.     char *source;        /* Characters to be displayed.  Need not
  453.                  * be NULL-terminated. */
  454.     int maxChars;        /* Maximum # of characters to consider from
  455.                  * source. */
  456.     int startX;            /* X-position at which first character will
  457.                  * be drawn. */
  458.     int maxX;            /* Don't consider any character that would
  459.                  * cross this x-position. */
  460.     int flags;            /* Various flag bits OR-ed together.
  461.                  * TK_WHOLE_WORDS means stop on a word boundary
  462.                  * (just before a space character) if
  463.                  * possible.  TK_AT_LEAST_ONE means always
  464.                  * return a value of at least one, even
  465.                  * if the character doesn't fit. 
  466.                  * TK_PARTIAL_OK means it's OK to display only
  467.                  * a part of the last character in the line.
  468.                  * TK_NEWLINES_NOT_SPECIAL means that newlines
  469.                  * are treated just like other control chars:
  470.                  * they don't terminate the line,*/
  471.     int *nextXPtr;        /* Return x-position of terminating
  472.                  * character here. */
  473. {
  474.     register TkFont *fontPtr;
  475.     register char *p;        /* Current character. */
  476.     register int c;
  477.     char *term;            /* Pointer to most recent character that
  478.                  * may legally be a terminating character. */
  479.     int termX;            /* X-position just after term. */
  480.     int curX;            /* X-position corresponding to p. */
  481.     int newX;            /* X-position corresponding to p+1. */
  482.     int type;
  483.  
  484.     /*
  485.      * Find the TkFont structure for this font, and make sure its
  486.      * font metrics exist.
  487.      */
  488.  
  489.     if (lastFontStructPtr == fontStructPtr) {
  490.     fontPtr = lastFontPtr;
  491.     } else {
  492.     Tcl_HashEntry *fontHashPtr;
  493.  
  494.     if (!initialized) {
  495.         badArg:
  496.         panic("TkMeasureChars received unknown font argument");
  497.     }
  498.     
  499.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  500.     if (fontHashPtr == NULL) {
  501.         goto badArg;
  502.     }
  503.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  504.     lastFontStructPtr = fontPtr->fontStructPtr;
  505.     lastFontPtr = fontPtr;
  506.     }
  507.     if (fontPtr->types == NULL) {
  508.     SetFontMetrics(fontPtr);
  509.     }
  510.  
  511.     /*
  512.      * Scan the input string one character at a time, until a character
  513.      * is found that crosses maxX.
  514.      */
  515.  
  516.     newX = curX = startX;
  517.     termX = 0;        /* Not needed, but eliminates compiler warning. */
  518.     term = source;
  519.     for (p = source, c = *p & 0xff; maxChars > 0; p++, maxChars--) {
  520.     type = fontPtr->types[c];
  521.     if (type == NORMAL) {
  522.         newX += fontPtr->widths[c];
  523.     } else if (type == TAB) {
  524.         newX += fontPtr->tabWidth;
  525.         newX -= newX % fontPtr->tabWidth;
  526.     } else if (type == REPLACE) {
  527.         replaceType:
  528.         newX += fontPtr->widths['\\'] + fontPtr->widths['x']
  529.             + fontPtr->widths[hexChars[(c >> 4) & 0xf]]
  530.             + fontPtr->widths[hexChars[c & 0xf]];
  531.     } else if (type == NEWLINE) {
  532.         if (flags & TK_NEWLINES_NOT_SPECIAL) {
  533.         goto replaceType;
  534.         }
  535.         break;
  536.     } else if (type != SKIP) {
  537.         panic("Unknown type %d in TkMeasureChars", type);
  538.     }
  539.     if (newX > maxX) {
  540.         break;
  541.     }
  542.     c = p[1] & 0xff;
  543.     if (isspace(c) || (c == 0)) {
  544.         term = p+1;
  545.         termX = newX;
  546.     }
  547.     curX = newX;
  548.     }
  549.  
  550.     /*
  551.      * P points to the first character that doesn't fit in the desired
  552.      * span.  Use the flags to figure out what to return.
  553.      */
  554.  
  555.     if ((flags & TK_PARTIAL_OK) && (curX < maxX)) {
  556.     curX = newX;
  557.     p++;
  558.     }
  559.     if ((flags & TK_AT_LEAST_ONE) && (term == source) && (maxChars > 0)
  560.          & !isspace(*term)) {
  561.     term = p;
  562.     termX = curX;
  563.     if (term == source) {
  564.         term++;
  565.         termX = newX;
  566.     }
  567.     } else if ((maxChars == 0) || !(flags & TK_WHOLE_WORDS)) {
  568.     term = p;
  569.     termX = curX;
  570.     }
  571.     *nextXPtr = termX;
  572.     return term-source;
  573. }
  574.  
  575. /*
  576.  *--------------------------------------------------------------
  577.  *
  578.  * TkDisplayChars --
  579.  *
  580.  *    Draw a string of characters on the screen, converting
  581.  *    tabs to the right number of spaces and control characters
  582.  *    to sequences of the form "\xhh" where hh are two hex
  583.  *    digits.
  584.  *
  585.  * Results:
  586.  *    None.
  587.  *
  588.  * Side effects:
  589.  *    Information gets drawn on the screen.
  590.  *
  591.  *--------------------------------------------------------------
  592.  */
  593.  
  594. void
  595. TkDisplayChars(display, drawable, gc, fontStructPtr, string, numChars,
  596.     x, y, flags)
  597.     Display *display;        /* Display on which to draw. */
  598.     Drawable drawable;        /* Window or pixmap in which to draw. */
  599.     GC gc;            /* Graphics context for actually drawing
  600.                  * characters. */
  601.     XFontStruct *fontStructPtr;    /* Font used in GC;  must have been allocated
  602.                  * by Tk_GetFontStruct.  Used to compute sizes
  603.                  * of tabs, etc. */
  604.     char *string;        /* Characters to be displayed. */
  605.     int numChars;        /* Number of characters to display from
  606.                  * string. */
  607.     int x, y;            /* Coordinates at which to draw string. */
  608.     int flags;            /* Flags to control display.  Only
  609.                  * TK_NEWLINES_NOT_SPECIAL is supported right
  610.                  * now.  See TkMeasureChars for information
  611.                  * about it. */
  612. {
  613.     register TkFont *fontPtr;
  614.     register char *p;        /* Current character being scanned. */
  615.     register int c;
  616.     int type;
  617.     char *start;        /* First character waiting to be displayed. */
  618.     int startX;            /* X-coordinate corresponding to start. */
  619.     int curX;            /* X-coordinate corresponding to p. */
  620.     char replace[10];
  621.  
  622.     /*
  623.      * Find the TkFont structure for this font, and make sure its
  624.      * font metrics exist.
  625.      */
  626.  
  627.     if (lastFontStructPtr == fontStructPtr) {
  628.     fontPtr = lastFontPtr;
  629.     } else {
  630.     Tcl_HashEntry *fontHashPtr;
  631.  
  632.     if (!initialized) {
  633.         badArg:
  634.         panic("TkDisplayChars received unknown font argument");
  635.     }
  636.     
  637.     fontHashPtr = Tcl_FindHashEntry(&fontTable, (char *) fontStructPtr);
  638.     if (fontHashPtr == NULL) {
  639.         goto badArg;
  640.     }
  641.     fontPtr = (TkFont *) Tcl_GetHashValue(fontHashPtr);
  642.     lastFontStructPtr = fontPtr->fontStructPtr;
  643.     lastFontPtr = fontPtr;
  644.     }
  645.     if (fontPtr->types == NULL) {
  646.     SetFontMetrics(fontPtr);
  647.     }
  648.  
  649.     /*
  650.      * Scan the string one character at a time.  Display control
  651.      * characters immediately, but delay displaying normal characters
  652.      * in order to pass many characters to the server all together.
  653.      */
  654.  
  655.     startX = curX = x;
  656.     start = string;
  657.     for (p = string; numChars > 0; numChars--, p++) {
  658.     c = *p & 0xff;
  659.     type = fontPtr->types[c];
  660.     if (type == NORMAL) {
  661.         curX += fontPtr->widths[c];
  662.         continue;
  663.     }
  664.     if (p != start) {
  665.         XDrawString(display, drawable, gc, startX, y, start, p - start);
  666.         startX = curX;
  667.     }
  668.     if (type == TAB) {
  669.         curX += fontPtr->tabWidth;
  670.         curX -= curX % fontPtr->tabWidth;
  671.     } else if (type == REPLACE) {
  672.         doReplace:
  673.         replace[0] = '\\';
  674.         replace[1] = 'x';
  675.         replace[2] = hexChars[(c >> 4) & 0xf];
  676.         replace[3] = hexChars[c & 0xf];
  677.         XDrawString(display, drawable, gc, startX, y, replace, 4);
  678.         curX += fontPtr->widths[replace[0]]
  679.             + fontPtr->widths[replace[1]]
  680.             + fontPtr->widths[replace[2]]
  681.             + fontPtr->widths[replace[3]];
  682.     } else if (type == NEWLINE) {
  683.         if (flags & TK_NEWLINES_NOT_SPECIAL) {
  684.         goto doReplace;
  685.         }
  686.         y += fontStructPtr->ascent + fontStructPtr->descent;
  687.         curX = x;
  688.     } else if (type != SKIP) {
  689.         panic("Unknown type %d in TkDisplayChars", type);
  690.     }
  691.     startX = curX;
  692.     start = p+1;
  693.     }
  694.  
  695.     /*
  696.      * At the very end, there may be one last batch of normal characters
  697.      * to display.
  698.      */
  699.  
  700.     if (p != start) {
  701.     XDrawString(display, drawable, gc, startX, y, start, p - start);
  702.     }
  703. }
  704.  
  705. /*
  706.  *----------------------------------------------------------------------
  707.  *
  708.  * TkUnderlineChars --
  709.  *
  710.  *    This procedure draws an underline for a given range of characters
  711.  *    in a given string, using appropriate information for the string's
  712.  *    font.  It doesn't draw the characters (which are assumed to have
  713.  *    been displayed previously);  it just draws the underline.
  714.  *
  715.  * Results:
  716.  *    None.
  717.  *
  718.  * Side effects:
  719.  *    Information gets displayed in "drawable".
  720.  *
  721.  *----------------------------------------------------------------------
  722.  */
  723.  
  724. void
  725. TkUnderlineChars(display, drawable, gc, fontStructPtr, string, x, y,
  726.     flags, firstChar, lastChar)
  727.     Display *display;        /* Display on which to draw. */
  728.     Drawable drawable;        /* Window or pixmap in which to draw. */
  729.     GC gc;            /* Graphics context for actually drawing
  730.                  * underline. */
  731.     XFontStruct *fontStructPtr;    /* Font used in GC;  must have been allocated
  732.                  * by Tk_GetFontStruct.  Used to character
  733.                  * dimensions, etc. */
  734.     char *string;        /* String containing characters to be
  735.                  * underlined. */
  736.     int x, y;            /* Coordinates at which first character of
  737.                  * string is drawn. */
  738.     int flags;            /* Flags that were passed to TkDisplayChars. */
  739.     int firstChar;        /* Index of first character to underline. */
  740.     int lastChar;        /* Index of last character to underline. */
  741. {
  742.     int xUnder, yUnder, width, height;
  743.     unsigned long value;
  744.  
  745.     /*
  746.      * First compute the vertical span of the underline, using font
  747.      * properties if they exist.
  748.      */
  749.  
  750.     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
  751.     yUnder = y + value;
  752.     } else {
  753.     yUnder = y + fontStructPtr->max_bounds.descent/2;
  754.     }
  755.     if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
  756.     height = value;
  757.     } else {
  758.     height = 2;
  759.     }
  760.  
  761.     /*
  762.      * Now compute the horizontal span of the underline.
  763.      */
  764.  
  765.     TkMeasureChars(fontStructPtr, string, firstChar, x, (int) 1000000, flags,
  766.         &xUnder);
  767.     TkMeasureChars(fontStructPtr, string+firstChar, lastChar+1-firstChar,
  768.         xUnder, (int) 1000000, flags, &width);
  769.     width -= xUnder;
  770.  
  771.     XFillRectangle(display, drawable, gc, xUnder, yUnder,
  772.         (unsigned int) width, (unsigned int) height);
  773. }
  774.